/*
 * TextServices / TSDotNet / TSNative
 * Marco Maria Bellocchi, Mauro Bampo
 * Copyright(c) 2013.
 * 
 * The MIT License
 * 
 * Permission is hereby granted, free of charge, to any person obtaining a copy
 * of this software and associated documentation files (the "Software"), to deal
 * in the Software without restriction, including without limitation the rights
 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
 * copies of the Software, and to permit persons to whom the Software is
 * furnished to do so, subject to the following conditions:
 * 
 * The above copyright notice and this permission notice shall be included in
 * all copies or substantial portions of the Software.
 * 
 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
 * THE SOFTWARE.
 * 
 */

// MB 19/01/2013: Created

#include "stdafx.h"
#include "Processor.h"
#include "Constants.h"

using namespace std;
using namespace TSNative::Filters;

Processor::Processor(vector<Filter*> filters) : _filters(filters) {
}

Processor::~Processor() {
	// Delete filters
	for (vector<Filter*>::iterator it = Filters().begin(); it!=Filters().end(); ++it)
		delete (*it);
}

inline vector<Filter*>& Processor::Filters() {
	return _filters;
}

FilterState Processor::ApplyFilter(Filter* pFilter, FilterState inputState, const char* pInput, int nInputLength,
	char** pOutput, int* pNOutputLength) {
	char* pBuffer = *pOutput;
	int nBufferSize = *pNOutputLength;
	bool reallocated = false;
	if(pBuffer == NULL || nBufferSize <= 0) {
		// Buffer not provided
		nBufferSize = pFilter->GetInitialBufferLength(nInputLength) + 2;
		pBuffer = new char[nBufferSize];
		reallocated = true;
	}
	*pNOutputLength = -1;
	FilterState outputState;
	bool done = false;
	while(!done) {
		pFilter->Reset();
		char* pOriginalBuffer = pBuffer;
		outputState = pFilter->Process(pInput, nInputLength, &pBuffer, nBufferSize, inputState);
		if(outputState.Overflow) {
#if _DEBUG
	printf("WARNING: Overflow in Processor::ApplyFilter.\n");
#endif
			// Overflow
			nBufferSize = nBufferSize * N_BUFFER_REALLOCATION_FACTOR;
			if(reallocated)
				// Don't delete the client's buffer
				delete[] pBuffer;
			pBuffer = new char[nBufferSize];
			reallocated = true;
			done = false;
		}
		else {
			if(reallocated && pBuffer != pOriginalBuffer)
				// Process reallocated
				delete[] pOriginalBuffer;
			done = true;
			*pOutput = pBuffer;
			*pNOutputLength = outputState.OutputIndex + 1;
		}
	}
	return outputState;
}

string Processor::Process(const string& value) {
	// Unwrap
	int nInputLength = value.length() + 1;
	const char* pInput = value.c_str();  // This should be constant according to docs
	// Loop over Process(char*) resizing buffer if needed
	char* pOutput = NULL;
	int nOutputLength = -1;
	ApplyFilter(this, FilterState(), pInput, nInputLength, &pOutput, &nOutputLength);
	// TODO: Avoid copy in construction?
	string result(pOutput, nOutputLength-1);  // Use number of significant chars
	delete[] pOutput;
	// The return _should_ NOT create a deep copy, thanks to RVO.
	return result;
}

PipelineProcessor::PipelineProcessor(vector<Filter*>& filters) : Processor(filters) {
}

int PipelineProcessor::GetInitialBufferLength(int nInputLength) {
	if(Filters().size() == 0)
		return nInputLength;  // Output := input
	int nBufSize = Filter::GetInitialBufferLength(nInputLength);  // Default
	for (vector<Filter*>::iterator it = Filters().begin(); it!=Filters().end(); ++it) {
		int nOutBufSize = (*it)->GetInitialBufferLength(nBufSize) + 2;
		if(nOutBufSize >= nBufSize)  // Don't decrease
			nBufSize = nOutBufSize;
	}
	return nBufSize;
}

FilterState PipelineProcessor::Process(const char* pInput, int inputLength, char** ppBuffer, int bufferLength, FilterState inputState) {
	FilterState currInputState = inputState, currOutputState;
	int nCurrInputLength = inputLength;
	char* pCurrInput = NULL;
	char* pCurrOutput = NULL;
	int nCurrOutputLength;
	// Compute max buffer size for reuse
	int nBufSize = GetInitialBufferLength(inputLength);
	pCurrInput = new char[nBufSize];
	memcpy(pCurrInput, pInput, inputLength*sizeof(char));
	if(Filters().size() == 0) {
		// Output := input
		*ppBuffer = pCurrInput;  // Because of last swap
		currOutputState.InputIndex = currOutputState.OutputIndex = inputLength-1;
		return currOutputState;
	}
	// ... else
	pCurrOutput = new char[nBufSize];
	nCurrOutputLength = nBufSize;
	for (vector<Filter*>::iterator it = Filters().begin(); it!=Filters().end(); ++it) {
		Filter* pFilter = *it;
		char* pOriginalOutput = pCurrOutput;
		currOutputState = ApplyFilter(pFilter, currInputState, pCurrInput, nCurrInputLength, &pCurrOutput, &nCurrOutputLength);
		if(pCurrOutput != pOriginalOutput)
			// ApplyFilter reallocated
			delete[] pOriginalOutput;
		// Swap
		char* pTemp = pCurrInput;
		pCurrInput = pCurrOutput;
		pCurrOutput = pTemp;
		nCurrInputLength = nCurrOutputLength;
		nCurrOutputLength = nBufSize;  // For the next iteration!
		//currInputState = currOutputState;  // NO: or the index will keep growing - keep original input state
		currInputState.InputIndex = currInputState.OutputIndex = 0;
	}
	// Destroy current unused buffer
	delete[] pCurrOutput;
	*ppBuffer = pCurrInput;  // Because of last swap
	return currOutputState;
}